Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add build for porting fetch to Node.js #1183

Merged
merged 3 commits into from
Jan 30, 2022
Merged

Conversation

targos
Copy link
Member

@targos targos commented Jan 29, 2022

In the discussion we had during the mini-summit, it was decided that we would try to include undici's fetch implementation in Node.js 18.

I just started experimenting with it and I figured it would be easier to discuss in a pull request, where we can directly try some things.

The esbuild output seems fine (I can push it here, but wanted to avoid too big of a diff), but we obviously have a problem with llhttp/WebAssembly.
Before I go too far in the wrong direction, what should we do about it? I can think of two solutions:

  • Adapt the part of undici that uses the llhttp parser to conditionally use the internal node binding. This is not trivial, as currently the native binding doesn't expose the exact same API that undici uses.
  • Inline the WASM code, like it was done for cjs-module-lexer. This seems risky because WebAssembly is not guaranteed to be available in a Node.js build.

/cc @nodejs/undici

@codecov-commenter
Copy link

codecov-commenter commented Jan 29, 2022

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 93.73%. Comparing base (2d03e77) to head (9e782c7).
Report is 1491 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1183   +/-   ##
=======================================
  Coverage   93.73%   93.73%           
=======================================
  Files          41       43    +2     
  Lines        4020     4022    +2     
=======================================
+ Hits         3768     3770    +2     
  Misses        252      252           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@targos
Copy link
Member Author

targos commented Jan 29, 2022

Adapt the part of undici that uses the llhttp parser to conditionally use the internal node binding

If we do that, it is possible to test it in userland. Just run node --expose-internals -r internal/test/binding and internalBinding('http_parser') is available.

@mcollina
Copy link
Member

Why are we bundling it? Wouldn't it be simpler to just add it to deps/ in core?

The best approach would be to use the node core bindings for http_parser. However I'm 100% happy to inline the wasm for now. I would require we use the internal binding for fetch to leave experimental.

@targos
Copy link
Member Author

targos commented Jan 29, 2022

Why are we bundling it? Wouldn't it be simpler to just add it to deps/ in core?

core doesn't support relative require calls. For example, const Client = require('./lib/client') would have to be replaced with const Client = require('internal/deps/undici/lib/client').
So we need some kind of source transformation anyway, and bundling was just easier for me to experiment.

@mcollina
Copy link
Member

Ah, that's true. Bundling is perfect.

@targos
Copy link
Member Author

targos commented Jan 29, 2022

However I'm 100% happy to inline the wasm for now.

I see that there are two different wasm builds. Do we need to bundle both?

@ronag
Copy link
Member

ronag commented Jan 29, 2022

I think the wasm version might be faster though? Can’t we look into getting that working somehow?

@targos
Copy link
Member Author

targos commented Jan 29, 2022

Should I look into inlining the WASM code for everyone or only for node core and keep the current behavior for the npm module?

@ronag
Copy link
Member

ronag commented Jan 29, 2022

I think future wise, it would be nice if Node core used a WASM build of llhttp instead of the native one in general.

It would be nice to keep the current behavior for the npm module.

@mcollina
Copy link
Member

However I'm 100% happy to inline the wasm for now.

I see that there are two different wasm builds. Do we need to bundle both?

I think you can just bundle the SIMD version. However we might need both for some architecture or in case of a backport. I'm not sure if the wasm simd is available everywhere.

@ronag
Copy link
Member

ronag commented Jan 29, 2022

@targos thank you for your effort on this!

@targos
Copy link
Member Author

targos commented Jan 29, 2022

Here's a Node PR based on this: nodejs/node#41749

@targos
Copy link
Member Author

targos commented Jan 29, 2022

So, the current state is that both WASM builds are serialized to base64 strings which are exported from two CJS modules (58kb each). I did not keep the current behavior for the npm module because I'm not sure yet how to do it.

@ronag
Copy link
Member

ronag commented Jan 29, 2022

I’m fine with this

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@merceyz
Copy link
Member

merceyz commented Jan 29, 2022

Should I look into inlining the WASM code for everyone or only for node core and keep the current behavior for the npm module?

Inlining it for everyone would make it easier for other projects that also needs to bundle their dependencies.
I experimented with using Undici in Yarn and had to bundle it using babel-plugin-static-fs which stopped working in later versions of Undici. Having Undici bundle it as a base64 string itself so we don't have to manually inline it and check the bundle into our repo would be greatly appreciated!

@ronag
Copy link
Member

ronag commented Jan 29, 2022

Is this ready to merge? @targos

package.json Outdated Show resolved Hide resolved
@targos
Copy link
Member Author

targos commented Jan 29, 2022

If it's fine for you, yes!

@ronag ronag merged commit 19b5789 into nodejs:main Jan 30, 2022
@targos targos deleted the build-node branch January 30, 2022 09:49
nodejs-github-bot pushed a commit to nodejs/node that referenced this pull request Feb 1, 2022
Fixes: #19393

PR-URL: #41749
Refs: nodejs/undici#1183
Reviewed-By: Antoine du Hamel <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Geoffrey Booth <[email protected]>
Reviewed-By: Robert Nagy <[email protected]>
Reviewed-By: Darshan Sen <[email protected]>
Reviewed-By: Mestery <[email protected]>
Reviewed-By: Mohammed Keyvanzadeh <[email protected]>
Reviewed-By: Gus Caplan <[email protected]>
Reviewed-By: Trivikram Kamat <[email protected]>
Reviewed-By: Myles Borins <[email protected]>
@gr2m
Copy link

gr2m commented Feb 1, 2022

Thank you. This is amazing. Thanks to everyone who made this happen 💖

@mcfedr mcfedr mentioned this pull request Feb 4, 2022
@miladfarca
Copy link

miladfarca commented Feb 4, 2022

Hello,
I just wanted to bring up a few items regarding Wasm and Simd execution.

I think you can just bundle the SIMD version.

Simd support in V8 has just been recently added to some architectures such as PPC and S390 and even then they are only supported on new hardware (i.e. PPC 9 and above). Running Simd code on older node versions or older hardware will just crash. It would be better to include both versions and fall back to regular Wasm code if Simd isn't supported.

I can see this module is is compiled with wasi-sdk: nodejs/llhttp#93
wasi-sdk doesn't seem to generate its own Javascript files to glue/call Wasm binaries and instead Js files need to be hand written per module. This could potentially cause endianness issues on big endian platforms like S390/AIX if Typed Arrays get used.

emscripten on the other hand does generate its own Js files and it's patched to handle endianness problems when dealing with Typed Arrays: emscripten-core/emscripten#13413

We are still working around some remaining problems related to its usage on BE machines but in general emscripten might be safer to use.
/cc @mhdawson

@ronag
Copy link
Member

ronag commented Feb 4, 2022

It would be better to include both versions and fall back to regular Wasm code if Simd isn't supported.

We already do this?

This could potentially cause endianness issues on big endian platforms like S390/AIX if Typed Arrays get used.

Do you have a concrete example?

PR welcome!

@miladfarca
Copy link

miladfarca commented Feb 4, 2022

We already do this?

Thanks for confirming.

Do you have a concrete example?

Wasm memory is LE even on BE machines. Typed Arrays however follow the machine byte order.
If a Typed Array uses Wasm memory buffer it could potently read garbage data or corrupt the data on BE machines as it doesn't enforce LE ordering:
https://github.com/nodejs/llhttp/blob/main/examples/wasm.ts#L197

emscripten has been patched (link in my previous comment) to use DataViews instead of Typed Arrays which has an option to force LE ordering.

If emscripten is used then all the generated Js files should also be safe to run on BE. If Js files are being written manually then the developer should make sure Typed Arrays wont cause an endianness problem.

@ronag
Copy link
Member

ronag commented Feb 4, 2022

Wouldn't it be easier to just fix the js code in the few places where the endianess is relevant?

@miladfarca
Copy link

We could take that approach per project/module (i'm just using llhttp as an example here). We have to make sure no usage of typed arrays is missed and test cases cover all the scenarios otherwise random failures could happen during production runs of a given module.

@ronag
Copy link
Member

ronag commented Feb 4, 2022

In the case of undici I don't think it's that complicated?

@miladfarca
Copy link

Doesn't seems like its too complex. If Typed Arrays such as this: https://github.com/nodejs/llhttp/blob/main/examples/wasm.ts#L197
Are only accessing byte sized data then it should be safe to use.

ruyadorno pushed a commit to nodejs/node that referenced this pull request Feb 8, 2022
Fixes: #19393

PR-URL: #41749
Refs: nodejs/undici#1183
Reviewed-By: Antoine du Hamel <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Geoffrey Booth <[email protected]>
Reviewed-By: Robert Nagy <[email protected]>
Reviewed-By: Darshan Sen <[email protected]>
Reviewed-By: Mestery <[email protected]>
Reviewed-By: Mohammed Keyvanzadeh <[email protected]>
Reviewed-By: Gus Caplan <[email protected]>
Reviewed-By: Trivikram Kamat <[email protected]>
Reviewed-By: Myles Borins <[email protected]>
@Kikobeats
Copy link
Contributor

Hey, I'm testing [email protected] and all is fine, just noted the original wasm files are shipped as part of the npm package:

drwxr-xr-x    - kikobeats staff 10 Feb 10:55 lib/llhttp/
.rw-r--r-- 4.2k kikobeats staff 23 Jan 17:43 ├── constants.d.ts
.rw-r--r--  11k kikobeats staff 23 Jan 17:43 ├── constants.js
.rw-r--r-- 7.0k kikobeats staff 23 Jan 17:43 ├── constants.js.map
.rwxr-xr-x  44k kikobeats staff 23 Jan 17:43 ├── llhttp.wasm*
.rw-r--r--  59k kikobeats staff  2 Feb 22:14 ├── llhttp.wasm.js
.rwxr-xr-x  44k kikobeats staff 23 Jan 17:43 ├── llhttp_simd.wasm*
.rw-r--r--  59k kikobeats staff  2 Feb 22:14 ├── llhttp_simd.wasm.js
.rw-r--r--  112 kikobeats staff 23 Jan 17:43 ├── utils.d.ts
.rw-r--r--  394 kikobeats staff 23 Jan 17:43 ├── utils.js
.rw-r--r--  432 kikobeats staff 23 Jan 17:43 └── utils.js.map

Since these files are now inline, could be possible they are no more necessary to be shipped with the library output?

that will be make undici a bit lightweight to install 🙂

@mcollina
Copy link
Member

could you send a PR to remove them?

@Kikobeats
Copy link
Contributor

@mcollina yay #1219

aduh95 pushed a commit to aduh95/node that referenced this pull request Apr 13, 2022
Fixes: nodejs#19393

PR-URL: nodejs#41749
Refs: nodejs/undici#1183
Reviewed-By: Antoine du Hamel <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Geoffrey Booth <[email protected]>
Reviewed-By: Robert Nagy <[email protected]>
Reviewed-By: Darshan Sen <[email protected]>
Reviewed-By: Mestery <[email protected]>
Reviewed-By: Mohammed Keyvanzadeh <[email protected]>
Reviewed-By: Gus Caplan <[email protected]>
Reviewed-By: Trivikram Kamat <[email protected]>
Reviewed-By: Myles Borins <[email protected]>
aduh95 pushed a commit to aduh95/node that referenced this pull request Apr 20, 2022
Fixes: nodejs#19393

PR-URL: nodejs#41749
Refs: nodejs/undici#1183
Reviewed-By: Antoine du Hamel <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Geoffrey Booth <[email protected]>
Reviewed-By: Robert Nagy <[email protected]>
Reviewed-By: Darshan Sen <[email protected]>
Reviewed-By: Mestery <[email protected]>
Reviewed-By: Mohammed Keyvanzadeh <[email protected]>
Reviewed-By: Gus Caplan <[email protected]>
Reviewed-By: Trivikram Kamat <[email protected]>
Reviewed-By: Myles Borins <[email protected]>
danielleadams pushed a commit to nodejs/node that referenced this pull request Apr 21, 2022
Fixes: #19393

PR-URL: #41749
Backport-PR-URL: #42727
Refs: nodejs/undici#1183
Reviewed-By: Antoine du Hamel <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Geoffrey Booth <[email protected]>
Reviewed-By: Robert Nagy <[email protected]>
Reviewed-By: Darshan Sen <[email protected]>
Reviewed-By: Mestery <[email protected]>
Reviewed-By: Mohammed Keyvanzadeh <[email protected]>
Reviewed-By: Gus Caplan <[email protected]>
Reviewed-By: Trivikram Kamat <[email protected]>
Reviewed-By: Myles Borins <[email protected]>
KhafraDev pushed a commit to KhafraDev/undici that referenced this pull request Jun 23, 2022
* feat: add build for porting to Node.js

* inline wasm

* Update package.json
RaisinTen added a commit to nodejs/postject that referenced this pull request Oct 10, 2022
Node.js core doesn't support relative require calls, so this change
provides a bundles API for Postject, so that it can be used easily
in Node.js. Undici too follows the same strategy.

Refs: nodejs/undici#1183
Signed-off-by: Darshan Sen <[email protected]>
RaisinTen added a commit to nodejs/postject that referenced this pull request Oct 12, 2022
* feat: provide bundled API

Node.js core doesn't support relative require calls, so this change
provides a bundles API for Postject, so that it can be used easily
in Node.js. Undici too follows the same strategy.

Refs: nodejs/undici#1183
Signed-off-by: Darshan Sen <[email protected]>

* fixup! test: bump timeout value from 60000 to 65000

Signed-off-by: Darshan Sen <[email protected]>

* fixup! test: bump timeout value from 65000 to 70000

Signed-off-by: Darshan Sen <[email protected]>

* fixup! fix: --output-api-header handling

Signed-off-by: Darshan Sen <[email protected]>

* fixup! refactor: bundling and copying to dist logic

Signed-off-by: Darshan Sen <[email protected]>

* fixup! chore: Error -> TypeError

Co-authored-by: David Sanders <[email protected]>

Signed-off-by: Darshan Sen <[email protected]>
Co-authored-by: David Sanders <[email protected]>
metcoder95 pushed a commit to metcoder95/undici that referenced this pull request Dec 26, 2022
* feat: add build for porting to Node.js

* inline wasm

* Update package.json
crysmags pushed a commit to crysmags/undici that referenced this pull request Feb 27, 2024
* feat: add build for porting to Node.js

* inline wasm

* Update package.json
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants